﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;

namespace GameLogic.Logic
{
    /// <summary>
    /// This Action will move the element according to Mouse movement.
    /// </summary>
    public class FollowObjectBehavior :  GameLoopBehavior<FrameworkElement>
    {
        public enum PositionOptions
        {
            None,
            X,
            Y,
            XY
        };

        // Private Properties
        private double maxX, maxY;
        private double minX, minY;

        private double width = 0;
        private double height = 0;
        private double oX = 0;
        private double oY = 0;

        // Vectors for Position and Velocity
        private Vector2D mousePosition = new Vector2D();
        private Vector2D targetPosition = new Vector2D();
        private Vector2D velocity = new Vector2D(0, 0);

        private UIElement parent;
        private FrameworkElement obj;

        #region Dependency Properties

        public static readonly DependencyProperty FollowPositionProperty = DependencyProperty.Register("FollowPosition", typeof(PositionOptions), typeof(FollowObjectBehavior), null);
        public static readonly DependencyProperty MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), typeof(FollowObjectBehavior), null);
        public static readonly DependencyProperty ObjectProperty = DependencyProperty.Register("FollowObject", typeof(string), typeof(FollowObjectBehavior), null);

        [Category("Follow Properties")]
        public PositionOptions FollowPosition
        {
            get { return (PositionOptions)this.GetValue(FollowPositionProperty); }
            set { this.SetValue(FollowPositionProperty, value); }
        }

        [Category("Follow Properties")]
        public Thickness Margin
        {
            get { return (Thickness)this.GetValue(MarginProperty); }
            set { this.SetValue(MarginProperty, value); }
        }

        [Category("Follow Properties")]
        public string FollowObject
        {
            get { return (string)this.GetValue(ObjectProperty); }
            set { this.SetValue(ObjectProperty, value); }
        }

        #endregion

        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.Loaded += this.OnAssociatedObjectLoaded;
        }

        private void OnAssociatedObjectLoaded(object sender, RoutedEventArgs e)
        {
            this.parent = VisualTreeHelper.GetParent(this.AssociatedObject) as UIElement;
            
            // Calculate Max/Min
            FrameworkElement container = parent as FrameworkElement;
            this.maxX = container.ActualWidth - this.AssociatedObject.ActualWidth - this.Margin.Right;
            this.minX = 0 + this.Margin.Left;
            this.maxY = container.ActualHeight - this.AssociatedObject.ActualHeight - this.Margin.Bottom;
            this.minY = 0 + this.Margin.Top;

            this.oX = this.AssociatedObject.RenderTransformOrigin.X;
            this.oY = this.AssociatedObject.RenderTransformOrigin.Y;
            this.width = this.AssociatedObject.Width;
            this.height = this.AssociatedObject.Height;

            this.targetPosition.X = Canvas.GetLeft(this.AssociatedObject) + (this.width * this.oX);
            this.targetPosition.Y = Canvas.GetTop(this.AssociatedObject) + (this.height * this.oX);

            // Get Object
            this.obj = (parent as FrameworkElement).FindName(FollowObject) as FrameworkElement;

            this.GameLoop.Update += this.HandleObjectMove;
        }

        private void HandleObjectMove(object sender, EventArgs e)
        {
            // Get Position of object to follow
            Point pt = obj.TransformToVisual(parent).Transform(new Point(0, 0));

            double newX = pt.X - obj.Width / 2 - width / 2;
            double newY = pt.Y - obj.Height / 2 - height / 2;

            newX = Math.Min(Math.Max(newX, this.minX), this.maxX);
            newY = Math.Min(Math.Max(newY, this.minY), this.maxY);

            if (this.FollowPosition != PositionOptions.None)
            {
                switch (this.FollowPosition)
                {
                    case PositionOptions.X:
                        Canvas.SetLeft(this.AssociatedObject, newX);
                        break;
                    case PositionOptions.Y:
                        Canvas.SetTop(this.AssociatedObject, newY);
                        break;
                    case PositionOptions.XY:
                        Canvas.SetLeft(this.AssociatedObject, newX);
                        Canvas.SetTop(this.AssociatedObject, newY);
                        break;
                }
            }
        }
    }
}
